import os
import MySQLdb
import MySQLWrapper
import logging
from time import time

from Gmetad.gmetad_plugin import GmetadPlugin
from Gmetad.gmetad_config import getConfig, GmetadConfig

def get_plugin():
    ''' All plugins are required to implement this method.  It is used as the factory
        function that instanciates a new plugin instance. '''
    # The plugin configuration ID that is passed in must match the section name
    #  in the configuration file.
    return MySQLPlugin('rrd')

class MySQLPlugin(GmetadPlugin):
    ''' This class implements the RRD plugin that stores metric data to RRD files.'''

    MySQLDB = 'MySQLDatabase'
    MySQLHost = 'MySQLHost'
    MySQLUser = 'MySQLUser'
    MySQLPassword = 'MySQLPassword'
    MetaTable = 'metric_meta_table'

    # Default RRAs
    _cfgDefaults = {
            MySQLDB : 'ganglia',
            MySQLHost : 'localhost',
            MySQLUser : 'ganglia',
            MySQLPassword : ''
    }

    def __init__(self, cfgid):
        self.cfg = None
        self._resetConfig()

        # The call to the parent class __init__ must be last
        GmetadPlugin.__init__(self, cfgid)
        self.InitMySQLConn()

    def _resetConfig(self):
        self.cfg = RRDPlugin._cfgDefaults

    def _parseConfig(self, cfgdata):
        '''This method overrides the plugin base class method.  It is used to
            parse the plugin specific configuration directives.'''
        for kw,args in cfgdata:
            if self._cfgDefaults.has_key(kw):
                self.cfg[kw] = args

    def InitMySQLConn():
        self.mysql = MySQLWrapper()
        self.mysql.setParam(self.cfg[MySQLPlugin.MySQLHost],
                            self.cfg[MySQLPlugin.MySQLUser],
                            self.cfg[MySQLPlugin.MySQLPassword],
                            self.cfg[MySQLPlugin.MySQLDB])
        self.mysql.connect()

    def _checkMetric(self, cluster, host, metricNode):
        metric = metricNode.getAttr('name')
        if not self.mysql.existItemPred(MetaTable,
              'cluster=\'%s\' and host=\'%s\''
              %(cluster, host)) :
          self.mysql.create('%s.%s'%(cluster, host),
                            '(metric varchar(64), /
                            time Date, /
                            value varchar(64))')

    def _createRRD(self, clusterNode, metricNode, rrdPath, step, summary):
        ''' This method creates a new metric RRD file.'''

        # Determine the RRD data source type.
        slope = metricNode.getAttr('slope')
        if slope.lower() == 'positive':
            dsType = 'COUNTER'
        else:
            dsType = 'GAUGE'

        localTime = clusterNode.getAttr('localtime')
        if localTime is None:
            localTime = int(time())

        # Calculate the heartbeat.
        heartbeat = 8*step
        # Format the data source string and add all of the RRDTool arguments to the
        #  args list.
        dsString = 'DS:sum:%s:%d:U:U'%(dsType,heartbeat)
        args = [str(rrdPath), '-b', str(localTime), '-s', str(step), str(dsString)]
        if summary is True:
            dsString = 'DS:num:%s:%d:U:U'%(dsType,heartbeat)
            args.append(str(dsString))
        for rra in self.cfg[RRDPlugin.RRAS]:
            args.append(rra)
        try:
            # Create the RRD file with the supplied args.
            rrdtool.create(*args)
            logging.debug('Created rrd %s'%rrdPath)
        except Exception, e:
            logging.info('Error creating rrd %s - %s'%(rrdPath, str(e)))

    def _updateMetric(self, metricTableName, clusterNode, metricNode):
        ''' This method updates an MySQL file with current metric values. '''
        # If the node has a time stamp then use it to update the MySQL.  Otherwise get
        #  the current timestamp.
        processTime = clusterNode.getAttr('localtime')
        if processTime is None:
            processTime = int(time())
        cmds = "INSERT INTO %s (time, value) VALUES (%s, %s)"
               % (metricTableName, str(processTime), str(metricNode.getAttr('val')))
        try:
            # Update the MySQL database with the current timestamp and value
            self.cursor.execute(cmds)
        except Exception, e:
            logging.info('Error updating MySQL table %s - %s'%(metricTableName, str(e)))

    def start(self):
        '''Called by the engine during initialization to get the plugin going.'''
        #print "MySQL start called"
        pass

    def stop(self):
        '''Called by the engine during shutdown to allow the plugin to shutdown.'''
        #print "MySQL stop called"
        self.mysql.disconnect()
        pass

    def notify(self, clusterNode):
        '''Called by the engine when the internal data source has changed.'''
        # Get the current configuration
        gmetadConfig = getConfig()
        # Find the data source configuration entry that matches the cluster name
        for ds in gmetadConfig[GmetadConfig.DATA_SOURCE]:
            if ds.name == clusterNode.getAttr('name'):
                break
        if ds is None:
            logging.info('No matching data source for %s'%clusterNode.getAttr('name'))
            return
        try:
            if clusterNode.getAttr('status') == 'down':
                return
        except AttributeError:
            pass
        # Create the cluster RRD base path and validate it
        cluster = clusterNode.getAttr('name')
        # Update metrics for each host in the cluster
        for hostNode in clusterNode:
            # Create the host RRD base path and validate it.
            host = hostNode.getAttr('name')
            # Update metrics for each host
            for metricNode in hostNode:
                # Don't update metrics that are not numeric values.
                if metricNode.getAttr('type') in ['string', 'timestamp']:
                    continue
                # Create the table final name and validate it.
                metricTableName = '%s.%s' % (cluster, host)
                self._checkMetric(cluster, host, metricNode)
                self._updateMetric(metricTableName, clusterNode, metricNode)
        #print "MySQL notify called"
